#pragma once

#include "base.h"


extern "C" void L8_Fatal(const char* file, int line, const char* format, ...);

// The FATAL, UNREACHABLE and UNIMPLEMENTED macros are useful during
// development, but they should not be relied on in the final product.
#ifdef DEBUG
#define FATAL(msg)                              \
    L8_Fatal(__FILE__, __LINE__, "%s", (msg))

#define UNIMPLEMENTED()                         \
    L8_Fatal(__FILE__, __LINE__, "unimplemented code")

#define UNREACHABLE()                           \
    L8_Fatal(__FILE__, __LINE__, "unreachable code")
#else

#define FATAL(msg)                              \
    L8_Fatal("", 0, "%s", (msg))

#define UNIMPLEMENTED()                         \
    L8_Fatal("", 0, "unimplemented code")

#define UNREACHABLE() ((void) 0)
#endif

// Used by the CHECK macro -- should not be called directly.
void CheckHelper(const char* file, int line, const char* source, bool condition);


// The CHECK macro checks that the given condition is true; if not, it
// prints a message to stderr and aborts.
#define CHECK(condition) CheckHelper(__FILE__, __LINE__, #condition, condition)


// Helper function used by the CHECK_EQ function when given int
// arguments.  Should not be called directly.
void CheckEqualsHelper(const char* file, int line, const char* expected_source, int expected,
                       const char* value_source, int value);


// Helper function used by the CHECK_NE function when given int
// arguments.  Should not be called directly.
void CheckNonEqualsHelper(const char* file, int line, const char* unexpected_source,
                          int unexpected, const char* value_source, int value);


// Helper function used by the CHECK function when given string
// arguments.  Should not be called directly.
void CheckEqualsHelper(const char* file, int line, const char* expected_source,
                       const char* expected, const char* value_source, const char* value);


void CheckNonEqualsHelper(const char* file, int line, const char* expected_source,
                          const char* expected, const char* value_source, const char* value);


// Helper function used by the CHECK function when given pointer
// arguments.  Should not be called directly.
void CheckEqualsHelper(const char* file, int line, const char* expected_source, void* expected,
                       const char* value_source, void* value);


void CheckNonEqualsHelper(const char* file, int line, const char* expected_source,
                          void* expected, const char* value_source, void* value);

void CheckEqualsHelper(const char* file, int line, const char* expected_source,
                       double expected, const char* value_source, double value);



#define CHECK_EQ(expected, value) CheckEqualsHelper(__FILE__, __LINE__, \
  #expected, expected, #value, value)


#define CHECK_NE(unexpected, value) CheckNonEqualsHelper(__FILE__, __LINE__, \
  #unexpected, unexpected, #value, value)


#define CHECK_GT(a, b) CHECK((a) > (b))
#define CHECK_GE(a, b) CHECK((a) >= (b))


// This is inspired by the static assertion facility in boost.  This
// is pretty magical.  If it causes you trouble on a platform you may
// find a fix in the boost code.
template <bool> class StaticAssertion;
template <> class StaticAssertion<true> { };
// This macro joins two tokens.  If one of the tokens is a macro the
// helper call causes it to be resolved before joining.
#define SEMI_STATIC_JOIN(a, b) SEMI_STATIC_JOIN_HELPER(a, b)
#define SEMI_STATIC_JOIN_HELPER(a, b) a##b
// Causes an error during compilation of the condition is not
// statically known to be true.  It is formulated as a typedef so that
// it can be used wherever a typedef can be used.  Beware that this
// actually causes each use to introduce a new defined type with a
// name depending on the source line.
template <int> class StaticAssertionHelper { };
#define STATIC_CHECK(test)                                                  \
  typedef                                                                   \
    StaticAssertionHelper<sizeof(StaticAssertion<static_cast<bool>(test)>)> \
    SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__)


// The ASSERT macro is equivalent to CHECK except that it only
// generates code in debug builds.  Ditto STATIC_ASSERT.
#ifdef DEBUG
#define ASSERT(condition)   CHECK(condition)
#define ASSERT_EQ(v1, v2)   CHECK_EQ(v1, v2)
#define ASSERT_NE(v1, v2)   CHECK_NE(v1, v2)
#define STATIC_ASSERT(test) STATIC_CHECK(test)
#define SLOW_ASSERT(condition) if (FLAG_enable_slow_asserts) CHECK(condition)
#define VERIFY(expr)        ASSERT(expr)
#else
#define ASSERT(condition)       ((void) 0)
#define ASSERT_EQ(v1, v2)       ((void) 0)
#define ASSERT_NE(v1, v2)       ((void) 0)
#define STATIC_ASSERT(test)     ((void) 0)
#define SLOW_ASSERT(condition)  ((void) 0)
#define VERIFY(expr)            (expr)
#endif


#define ASSERT_TAG_ALIGNED(address) \
  ASSERT((reinterpret_cast<intptr_t>(address) & kHeapObjectTagMask) == 0)

#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & kHeapObjectTagMask) == 0)

#define ASSERT_NOT_NULL(p)  ASSERT_NE(NULL, p)
